﻿using System;
using System.Collections.Generic;
using System.Text;
using Emerson.Storage.Agent.Core;
using System.Net;
using System.Configuration;
using Emerson.Storage.Agent.DeviceAccess;
using System.Reflection;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;

namespace Emerson.Storage.Agent
{
    class AgentService : MarshalByRefObject, IAgentService
    {
        /// <summary>
        /// Contains the batteries loaded in current assembly.
        /// </summary>
        static private List<IBatteryAccess> loadedBatteries;
        /// <summary>
        /// loadedBatteries might accessed by multiple threads, so we need a locker here
        /// </summary>
        static object loadedBatteriesLocker = new object();

        static AgentService()
        {
            int agentPort = int.Parse(ConfigurationManager.AppSettings["AgentPort"]);

            TcpServerChannel channel = new TcpServerChannel("AgentChannel", agentPort);
            ChannelServices.RegisterChannel(channel, false);
            RemotingConfiguration.RegisterWellKnownServiceType(
                   typeof(AgentService), "EmersonStorageAgent",
                   WellKnownObjectMode.Singleton);

            loadedBatteries = new List<IBatteryAccess>();
        }

        public AgentService()
        {
        }

        #region IAgentService Members

        /// <summary>
        /// Gets the host workstation info.
        /// </summary>
        public WorkstationInfo GetWorkstationInfo()
        {
            WorkstationInfo wi = new WorkstationInfo();
            wi.Name = Environment.MachineName;
            wi.OSVersion = Environment.OSVersion.ToString();
            IPAddress[] addresses = Dns.GetHostAddresses(wi.Name);

            for (int i = 0; i < addresses.Length; i++)
            {
                if (addresses[i].AddressFamily.ToString().Equals("InterNetwork"))
                {
                    wi.IPAddress = addresses[i].ToString();
                    break;
                }
            }
            return wi;
        }

        /// <summary>
        /// Gets the batteries that are loaded in the current agent.
        /// </summary>
        /// <returns></returns>
        public List<BatteryInfo> GetBatteryInfos()
        {
            List<BatteryInfo> batteries = new List<BatteryInfo>();

            lock (loadedBatteriesLocker)
            {
                foreach (var bat in loadedBatteries)
                {
                    BatteryInfo bi = bat.GetBatteryInfo();
                    batteries.Add(bi);
                }
            }

            return batteries;
        }

        public void ScanDevice()
        {
            List<string> detectedDevice = new List<string>();
            string[] deviceFiles = Directory.GetFiles(".\\Devices", "*.dll", SearchOption.AllDirectories);

            //load all assemblies before executing them, because some assebmlies depends on another.
            //there must be better ways to do this.
            List<Assembly> loadedAssemblies = new List<Assembly>();
            foreach (var file in deviceFiles)
            {
                try
                {
                    Assembly curAssembly = Assembly.LoadFrom(file);
                    loadedAssemblies.Add(curAssembly);
                }
                catch (Exception)
                {
                    // do nothing for an invalid dll
                }
            }

            foreach (Assembly curAssembly in loadedAssemblies)
            {
                try
                {
                    IBatteryAccess bat = FindBatteryInterface(curAssembly);

                    lock (loadedBatteriesLocker)
                    {
                        loadedBatteries.Add(bat);
                    }
                }
                catch (Exception)
                {
                    //do nothing if current device failed (does not contain IBatteryAccess interface)
                }
            }
        }

        private IBatteryAccess FindBatteryInterface(Assembly curAssembly)
        {
            string interfaceName = typeof(IBatteryAccess).ToString();

            //Search in this assembly for any types that inherits from IBatteryAccess.
            //if there is such type, Then this assembly can be used to connect to a specified battery.
            Type[] types = curAssembly.GetExportedTypes();
            foreach (Type type in types)
            {
                Type interfaceType = type.GetInterface(interfaceName);

                if (interfaceType != null)
                {
                    object obj = curAssembly.CreateInstance(type.FullName);
                    IBatteryAccess batteryAccess = (IBatteryAccess)obj;
                    return batteryAccess;
                }
            }

            throw new Exception("Cannot find IBatterAccess in current assembly");
        }

        public void Restart()
        {
            ProcessStartInfo startinfo = new ProcessStartInfo("shutdown", "-r");
            Process.Start(startinfo);
        }

        public void Shutdown()
        {
            ProcessStartInfo startinfo = new ProcessStartInfo("shutdown", "-s");
            Process.Start(startinfo);
        }

        #endregion
    }
}